#include "wx/wxprec.h"
#include "wx/apptrait.h"
#include "wx/process.h"
#include "wx/txtstrm.h"
#include "wx/uri.h"
#include "wx/mimetype.h"
#include "wx/config.h"

#if defined(__WXWINCE__) && wxUSE_DATETIME
#include "wx/datetime.h"
#endif

#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#if !wxONLY_WATCOM_EARLIER_THAN(1,4)
#if !(defined(_MSC_VER) && (_MSC_VER > 800))
#include <errno.h>
#endif
#endif

#include "wx/colordlg.h"
#include "wx/fontdlg.h"
#include "wx/notebook.h"
#include "wx/statusbr.h"
#include <time.h>

#if !defined(__MWERKS__) && !defined(__WXWINCE__)
#include <sys/types.h>
#include <sys/stat.h>
#endif

#if wxUSE_BASE

static wxChar hexArray[] = wxT( "0123456789ABCDEF" );

int wxHexToDec( const wxString& buf ) {
  int firstDigit, secondDigit;
  if( buf.GetChar( 0 ) >= wxT( 'A' ) ) {
    firstDigit = buf.GetChar( 0 ) - wxT( 'A' ) + 10;
  } else {
    firstDigit = buf.GetChar( 0 ) - wxT( '0' );
  }
  if( buf.GetChar( 1 ) >= wxT( 'A' ) ) {
    secondDigit = buf.GetChar( 1 ) - wxT( 'A' ) + 10;
  } else {
    secondDigit = buf.GetChar( 1 ) - wxT( '0' );
  }
  return ( firstDigit & 0xF ) * 16 + ( secondDigit & 0xF );
}

void wxDecToHex( int dec, wxChar *buf ) {
  int firstDigit = ( int )( dec / 16.0 );
  int secondDigit = ( int )( dec - ( firstDigit * 16.0 ) );
  buf[0] = hexArray[firstDigit];
  buf[1] = hexArray[secondDigit];
  buf[2] = 0;
}

wxString wxDecToHex( int dec ) {
  wxChar buf[3];
  wxDecToHex( dec, buf );
  return wxString( buf );
}

wxString wxNow() {
  #ifdef __WXWINCE__
#error mmmm
  #if wxUSE_DATETIME
  wxDateTime now = wxDateTime::Now();
  return now.Format();
  #else
  return wxEmptyString;
  #endif
  #else
  time_t now = time( ( time_t * ) NULL );
  char *date = ctime( &now );
  date[24] = '\0';
  return wxString::FromAscii( date );
  #endif
}

void wxUsleep( unsigned long milliseconds ) {
  wxMilliSleep( milliseconds );
}

const wxChar *wxGetInstallPrefix() {
  wxString prefix;
  if( wxGetEnv( wxT( "WXPREFIX" ), &prefix ) ) {
    return prefix.c_str();
  }
  #ifdef wxINSTALL_PREFIX
  return wxT( wxINSTALL_PREFIX );
  #else
  return wxEmptyString;
  #endif
}

wxString wxGetDataDir() {
  wxString dir = wxGetInstallPrefix();
  dir <<  wxFILE_SEP_PATH << wxT( "share" ) << wxFILE_SEP_PATH << wxT( "wx" );
  return dir;
}

bool wxIsPlatformLittleEndian() {
  union {
    long l;
    char c[sizeof( long )];
  } u;
  u.l = 1;
  return u.c[0] == 1;
}

wxArrayInt* wxPlatform::sm_customPlatforms = NULL;

void wxPlatform::Copy( const wxPlatform& platform ) {
  m_longValue = platform.m_longValue;
  m_doubleValue = platform.m_doubleValue;
  m_stringValue = platform.m_stringValue;
}

wxPlatform wxPlatform::If( int platform, long value ) {
  if( Is( platform ) ) {
    return wxPlatform( value );
  } else {
    return wxPlatform();
  }
}

wxPlatform wxPlatform::IfNot( int platform, long value ) {
  if( !Is( platform ) ) {
    return wxPlatform( value );
  } else {
    return wxPlatform();
  }
}

wxPlatform& wxPlatform::ElseIf( int platform, long value ) {
  if( Is( platform ) ) {
    m_longValue = value;
  }
  return *this;
}

wxPlatform& wxPlatform::ElseIfNot( int platform, long value ) {
  if( !Is( platform ) ) {
    m_longValue = value;
  }
  return *this;
}

wxPlatform wxPlatform::If( int platform, double value ) {
  if( Is( platform ) ) {
    return wxPlatform( value );
  } else {
    return wxPlatform();
  }
}

wxPlatform wxPlatform::IfNot( int platform, double value ) {
  if( !Is( platform ) ) {
    return wxPlatform( value );
  } else {
    return wxPlatform();
  }
}

wxPlatform& wxPlatform::ElseIf( int platform, double value ) {
  if( Is( platform ) ) {
    m_doubleValue = value;
  }
  return *this;
}

wxPlatform& wxPlatform::ElseIfNot( int platform, double value ) {
  if( !Is( platform ) ) {
    m_doubleValue = value;
  }
  return *this;
}

wxPlatform wxPlatform::If( int platform, const wxString& value ) {
  if( Is( platform ) ) {
    return wxPlatform( value );
  } else
  { return wxPlatform(); }
}

wxPlatform wxPlatform::IfNot( int platform, const wxString& value ) {
  if( !Is( platform ) ) {
    return wxPlatform( value );
  } else {
    return wxPlatform();
  }
}

wxPlatform& wxPlatform::ElseIf( int platform, const wxString& value ) {
  if( Is( platform ) ) {
    m_stringValue = value;
  }
  return *this;
}

wxPlatform& wxPlatform::ElseIfNot( int platform, const wxString& value ) {
  if( !Is( platform ) ) {
    m_stringValue = value;
  }
  return *this;
}

wxPlatform& wxPlatform::Else( long value ) {
  m_longValue = value;
  return *this;
}

wxPlatform& wxPlatform::Else( double value ) {
  m_doubleValue = value;
  return *this;
}

wxPlatform& wxPlatform::Else( const wxString& value ) {
  m_stringValue = value;
  return *this;
}

void wxPlatform::AddPlatform( int platform ) {
  if( !sm_customPlatforms ) {
    sm_customPlatforms = new wxArrayInt;
  }
  sm_customPlatforms->Add( platform );
}

void wxPlatform::ClearPlatforms() {
  delete sm_customPlatforms;
  sm_customPlatforms = NULL;
}

bool wxPlatform::Is( int platform ) {
  if( platform == wxPORT_GTK ) {
    return true;
  }
  if( platform == wxOS_UNIX ) {
    return true;
  }
  if( sm_customPlatforms && sm_customPlatforms->Index( platform ) != wxNOT_FOUND ) {
    return true;
  }
  return false;
}

bool wxGetEmailAddress( wxChar *address, int maxSize ) {
  wxString email = wxGetEmailAddress();
  if( !email ) {
    return false;
  }
  wxStrncpy( address, email, maxSize - 1 );
  address[maxSize - 1] = wxT( '\0' );
  return true;
}

wxString wxGetEmailAddress() {
  wxString email;
  wxString host = wxGetFullHostName();
  if( !host.empty() ) {
    wxString user = wxGetUserId();
    if( !user.empty() ) {
      email << user << wxT( '@' ) << host;
    }
  }
  return email;
}

wxString wxGetUserId() {
  static const int maxLoginLen = 256;
  wxString buf;
  bool ok = wxGetUserId( wxStringBuffer( buf, maxLoginLen ), maxLoginLen );
  if( !ok ) {
    buf.Empty();
  }
  return buf;
}

wxString wxGetUserName() {
  static const int maxUserNameLen = 1024;
  wxString buf;
  bool ok = wxGetUserName( wxStringBuffer( buf, maxUserNameLen ), maxUserNameLen );
  if( !ok ) {
    buf.Empty();
  }
  return buf;
}

wxString wxGetHostName() {
  static const size_t hostnameSize = 257;
  wxString buf;
  bool ok = wxGetHostName( wxStringBuffer( buf, hostnameSize ), hostnameSize );
  if( !ok ) {
    buf.Empty();
  }
  return buf;
}

wxString wxGetFullHostName() {
  static const size_t hostnameSize = 257;
  wxString buf;
  bool ok = wxGetFullHostName( wxStringBuffer( buf, hostnameSize ), hostnameSize );
  if( !ok ) {
    buf.Empty();
  }
  return buf;
}

wxString wxGetHomeDir() {
  wxString home;
  wxGetHomeDir( &home );
  return home;
}

static bool ReadAll( wxInputStream *is, wxArrayString& output ) {
  wxCHECK_MSG( is, false, _T( "NULL 流入 wxExecute()?" ) );
  is->Reset();
  wxTextInputStream tis( *is );
  for( ;; ) {
    wxString line = tis.ReadLine();
    if( is->Eof() ) {
      if( !line.empty() ) {
        output.Add( line );
      }
      break;
    }
    if( !*is ) {
      return false;
    }
    output.Add( line );
  }
  return true;
}

static long wxDoExecuteWithCapture( const wxString& command, wxArrayString& output, wxArrayString* error, int flags ) {
  wxProcess *process = new wxProcess;
  process->Redirect();
  long rc = wxExecute( command, wxEXEC_SYNC | flags, process );
  #if wxUSE_STREAMS
  if( rc != -1 ) {
    if( !ReadAll( process->GetInputStream(), output ) ) {
      rc = -1;
    }
    if( error ) {
      if( !ReadAll( process->GetErrorStream(), *error ) ) {
        rc = -1;
      }
    }
  }
  #else
  wxUnusedVar( output );
  wxUnusedVar( error );
  #endif // wxUSE_STREAMS/!wxUSE_STREAMS
  delete process;
  return rc;
}

long wxExecute( const wxString& command, wxArrayString& output, int flags ) {
  return wxDoExecuteWithCapture( command, output, NULL, flags );
}

long wxExecute( const wxString& command,
                wxArrayString& output,
                wxArrayString& error,
                int flags ) {
  return wxDoExecuteWithCapture( command, output, &error, flags );
}

#include "wx/private/browserhack28.h"

static bool wxLaunchDefaultBrowserBaseImpl( const wxString& url, int flags );

static wxLaunchDefaultBrowserImpl_t s_launchBrowserImpl = &wxLaunchDefaultBrowserBaseImpl;

void wxSetLaunchDefaultBrowserImpl( wxLaunchDefaultBrowserImpl_t newImpl ) {
  s_launchBrowserImpl = newImpl != NULL ? newImpl : &wxLaunchDefaultBrowserBaseImpl;
}

static bool wxLaunchDefaultBrowserBaseImpl( const wxString& url, int flags ) {
  wxUnusedVar( flags );
  #if defined(__WXMSW__)
  #elif defined(__WXMAC__)
  #else
  wxString path, xdg_open;
  if( wxGetEnv( _T( "PATH" ), &path ) &&
      wxFindFileInPath( &xdg_open, path, _T( "xdg-open" ) ) ) {
    if( wxExecute( xdg_open + _T( " " ) + url ) ) {
      return true;
    }
  }
  wxString desktop = wxTheApp->GetTraits()->GetDesktopEnvironment();
  if( desktop == wxT( "GNOME" ) ) {
    wxArrayString errors;
    wxArrayString output;
    long res = wxExecute( wxT( "gconftool-2 --get /desktop/gnome/applications/browser/exec" ),
                          output, errors, wxEXEC_NODISABLE );
    if( res >= 0 && errors.GetCount() == 0 ) {
      wxString cmd = output[0];
      cmd << _T( ' ' ) << url;
      if( wxExecute( cmd ) ) {
        return true;
      }
    }
  } else if( desktop == wxT( "KDE" ) ) {
    if( wxExecute( wxT( "kfmclient openURL " ) + url ) ) {
      return true;
    }
  }
  bool ok = false;
  wxString cmd;
  #if wxUSE_MIMETYPE
  wxFileType *ft = wxTheMimeTypesManager->GetFileTypeFromExtension( _T( "html" ) );
  if( ft ) {
    wxString mt;
    ft->GetMimeType( &mt );
    ok = ft->GetOpenCommand( &cmd, wxFileType::MessageParameters( url ) );
    delete ft;
  }
  #endif // wxUSE_MIMETYPE
  if( !ok || cmd.empty() ) {
    cmd = wxGetenv( wxT( "BROWSER" ) );
    if( !cmd.empty() ) {
      cmd << _T( ' ' ) << url;
    }
  }
  ok = ( !cmd.empty() && wxExecute( cmd ) );
  if( ok ) {
    return ok;
  }
  wxLogError( _T( "No default application configured for HTML files." ) );
  #endif
  return false;
}

bool wxLaunchDefaultBrowser( const wxString& urlOrig, int flags ) {
  wxString url( urlOrig );
  wxURI uri( url );
  if( !uri.HasScheme() ) {
    if( wxFileExists( urlOrig ) ) {
      url.Prepend( wxT( "file://" ) );
    } else
    { url.Prepend( wxT( "http://" ) ); }
  }
  if( s_launchBrowserImpl( url, flags ) ) {
    return true;
  }
  wxLogSysError( _T( "Failed to open URL \"%s\" in default browser." ),
                 url.c_str() );
  return false;
}

bool wxYield() {
  return wxTheApp && wxTheApp->Yield();
}

bool wxYieldIfNeeded() {
  return wxTheApp && wxTheApp->Yield( true );
}

#endif // wxUSE_BASE

static long wxCurrentId = 100;

long wxNewId() {
  if( wxCurrentId == wxID_LOWEST ) {
    wxCurrentId = wxID_HIGHEST + 1;
  }
  return wxCurrentId++;
}

long
wxGetCurrentId( void ) { return wxCurrentId; }

void
wxRegisterId( long id ) {
  if( id >= wxCurrentId ) {
    wxCurrentId = id + 1;
  }
}

wxChar *wxStripMenuCodes( const wxChar *in, wxChar *out ) {
  wxString s = wxMenuItem::GetLabelFromText( in );
  if( out ) {
    memcpy( out, s.c_str(), s.length() * sizeof( wxChar ) );
  } else {
    out = new wxChar[s.length() + 1];
    wxStrcpy( out, s.c_str() );
  }
  return out;
}

wxString wxStripMenuCodes( const wxString& in, int flags ) {
  wxASSERT_MSG( flags, _T( "this is useless to call without any flags" ) );
  wxString out;
  size_t len = in.length();
  out.reserve( len );
  for( size_t n = 0; n < len; n++ ) {
    wxChar ch = in[n];
    if( ( flags & wxStrip_Mnemonics ) && ch == _T( '&' ) ) {
      if( ++n == len ) {
        wxLogDebug( _T( "菜单字符串无效 '%s'" ), in.c_str() );
      } else
      { ch = in[n]; }
    } else if( ( flags & wxStrip_Accel ) && ch == _T( '\t' ) ) {
      break;
    }
    out += ch;
  }
  return out;
}

wxWindow * wxFindWindowByLabel( const wxString& title, wxWindow * parent ) {
  return wxWindow::FindWindowByLabel( title, parent );
}

wxWindow * wxFindWindowByName( const wxString& name, wxWindow * parent ) {
  return wxWindow::FindWindowByName( name, parent );
}

int
wxFindMenuItemId( wxFrame * frame, const wxString& menuString, const wxString& itemString ) {
  wxMenuBar *menuBar = frame->GetMenuBar();
  if( menuBar ) {
    return menuBar->FindMenuItem( menuString, itemString );
  }
  return wxNOT_FOUND;
}

wxWindow* wxFindWindowAtPoint( wxWindow* win, const wxPoint& pt ) {
  if( !win->IsShown() ) {
    return NULL;
  }
  if( win->IsKindOf( CLASSINFO( wxNotebook ) ) ) {
    wxNotebook* nb = ( wxNotebook* ) win;
    int sel = nb->GetSelection();
    if( sel >= 0 ) {
      wxWindow* child = nb->GetPage( sel );
      wxWindow* foundWin = wxFindWindowAtPoint( child, pt );
      if( foundWin ) {
        return foundWin;
      }
    }
  }
  wxWindowList::compatibility_iterator node = win->GetChildren().GetLast();
  while( node ) {
    wxWindow* child = node->GetData();
    wxWindow* foundWin = wxFindWindowAtPoint( child, pt );
    if( foundWin ) {
      return foundWin;
    }
    node = node->GetPrevious();
  }
  wxPoint pos = win->GetPosition();
  wxSize sz = win->GetSize();
  if( !win->IsTopLevel() && win->GetParent() ) {
    pos = win->GetParent()->ClientToScreen( pos );
  }
  wxRect rect( pos, sz );
  if( rect.Contains( pt ) ) {
    return win;
  }
  return NULL;
}

wxWindow* wxGenericFindWindowAtPoint( const wxPoint& pt ) {
  wxWindowList::compatibility_iterator node = wxTopLevelWindows.GetLast();
  while( node ) {
    wxWindow* win = node->GetData();
    wxWindow* found = wxFindWindowAtPoint( win, pt );
    if( found ) {
      return found;
    }
    node = node->GetPrevious();
  }
  return NULL;
}

int wxMessageBox( const wxString& message, const wxString& caption, long style, wxWindow *parent, int ( x ), int ( y ) ) {
  long decorated_style = style;
  if( ( style & ( wxICON_EXCLAMATION | wxICON_HAND | wxICON_INFORMATION | wxICON_QUESTION ) ) == 0 ) {
    decorated_style |= ( style & wxYES ) ? wxICON_QUESTION : wxICON_INFORMATION ;
  }
  wxMessageDialog dialog( parent, message, caption, decorated_style );
  int ans = dialog.ShowModal();
  switch( ans ) {
    case wxID_OK:
      return wxOK;
    case wxID_YES:
      return wxYES;
    case wxID_NO:
      return wxNO;
    case wxID_CANCEL:
      return wxCANCEL;
  }
  wxFAIL_MSG( _T( "unexpected return code from wxMessageDialog" ) );
  return wxCANCEL;
}

wxString wxGetTextFromUser( const wxString& message, const wxString& caption,
                            const wxString& defaultValue, wxWindow *parent,
                            wxCoord x, wxCoord y, bool centre ) {
  wxString str;
  long style = wxTextEntryDialogStyle;
  if( centre ) {
    style |= wxCENTRE;
  } else
  { style &= ~wxCENTRE; }
  wxTextEntryDialog dialog( parent, message, caption, defaultValue, style, wxPoint( x, y ) );
  if( dialog.ShowModal() == wxID_OK ) {
    str = dialog.GetValue();
  }
  return str;
}

wxString wxGetPasswordFromUser( const wxString& message, const wxString& caption, const wxString& defaultValue,
                                wxWindow *parent, wxCoord x, wxCoord y, bool centre ) {
  wxString str;
  long style = wxTextEntryDialogStyle;
  if( centre ) {
    style |= wxCENTRE;
  } else {
    style &= ~wxCENTRE;
  }
  wxPasswordEntryDialog dialog( parent, message, caption, defaultValue,
                                style, wxPoint( x, y ) );
  if( dialog.ShowModal() == wxID_OK ) {
    str = dialog.GetValue();
  }
  return str;
}

wxColour wxGetColourFromUser( wxWindow *parent, const wxColour& colInit, const wxString& caption ) {
  static wxColourData data;
  data.SetChooseFull( true );
  if( colInit.Ok() ) {
    data.SetColour( ( wxColour & )colInit );
  }
  wxColour colRet;
  wxColourDialog dialog( parent, &data );
  if( !caption.empty() ) {
    dialog.SetTitle( caption );
  }
  if( dialog.ShowModal() == wxID_OK ) {
    colRet = dialog.GetColourData().GetColour();
  }
  return colRet;
}

wxFont wxGetFontFromUser( wxWindow *parent, const wxFont& fontInit, const wxString& caption ) {
  wxFontData data;
  if( fontInit.Ok() ) {
    data.SetInitialFont( fontInit );
  }
  wxFont fontRet;
  wxFontDialog dialog( parent, data );
  if( !caption.empty() ) {
    dialog.SetTitle( caption );
  }
  if( dialog.ShowModal() == wxID_OK ) {
    fontRet = dialog.GetFontData().GetChosenFont();
  }
  return fontRet;
}

void wxEnableTopLevelWindows( bool enable ) {
  wxWindowList::compatibility_iterator node;
  for( node = wxTopLevelWindows.GetFirst(); node; node = node->GetNext() ) {
    node->GetData()->Enable( enable );
  }
}

wxWindowDisabler::wxWindowDisabler( wxWindow *winToSkip ) {
  m_winDisabled = NULL;
  wxWindowList::compatibility_iterator node;
  for( node = wxTopLevelWindows.GetFirst(); node; node = node->GetNext() ) {
    wxWindow *winTop = node->GetData();
    if( winTop == winToSkip ) {
      continue;
    }
    if( winTop->IsEnabled() && winTop->IsShown() ) {
      winTop->Disable();
    } else {
      if( !m_winDisabled ) {
        m_winDisabled = new wxWindowList;
      }
      m_winDisabled->Append( winTop );
    }
  }
}

wxWindowDisabler::~wxWindowDisabler() {
  wxWindowList::compatibility_iterator node;
  for( node = wxTopLevelWindows.GetFirst(); node; node = node->GetNext() ) {
    wxWindow *winTop = node->GetData();
    if( !m_winDisabled || !m_winDisabled->Find( winTop ) ) {
      winTop->Enable();
    }
  }
  delete m_winDisabled;
}

bool wxSafeYield( wxWindow *win, bool onlyIfNeeded ) {
  wxWindowDisabler wd( win );
  bool rc;
  if( onlyIfNeeded ) {
    rc = wxYieldIfNeeded();
  } else
  { rc = wxYield(); }
  return rc;
}
